#include "SocketStickyPacketDemo.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include "string.h"

using namespace std;

void readClientMsg(SOCKET clientSocket, sockaddr_in *clientAddr)
{
    stringstream clientInfo;
    clientInfo << "客户端（" << inet_ntoa(clientAddr->sin_addr) << ":" << ntohs(clientAddr->sin_port) << "）";

    char msg[4096];
    while (true)
    {
        memset(msg, 0, sizeof(msg));
        int len = recvMsg(clientSocket, msg, sizeof(msg));
        if (len > 0)
        {
            cout << clientInfo.str() << "，接收数据(" << len << "):" << msg << endl;
        }
        else
        {
            break;
        }
        sleep(1);
    }
}

void socketStickyPacketServiceDemo()
{
    cout << "案例四：TCP粘包问题及解决演示（服务器端）" << endl;
#ifdef _WIN32
    // windows 下需要装载网络访问库
    WSADATA lpWSAData;
    WSAStartup(MAKEWORD(2, 2), &lpWSAData);
#endif

    // 1. 创建嵌套字
    SOCKET serverSocket = createSocket();
    if (serverSocket == -1)
    {
        return;
    }

    // 2. 绑定并设置监听
    int listenRet = setListen(serverSocket, 9999);
    if (listenRet == -1)
    {
        closeSocket(serverSocket);
        return;
    }

    // 3. 阻塞等待客户端链接
    // while (true)
    // {
    sockaddr_in clientAddr;
    SOCKET clientSocket = acceptConn(serverSocket, &clientAddr);
    if (clientSocket == -1)
    {
        closeSocket(serverSocket);
        return;
    }
    readClientMsg(clientSocket, &clientAddr);
    // }

    closeSocket(clientSocket);
    closeSocket(serverSocket);

#ifdef _WIN32
    // windows 平台下结束了还需要移除网络库的装载
    WSACleanup();
#endif
}

void socketStickyPacketClientDemo()
{
    cout << "案例四：TCP粘包问题及解决演示（客户端）" << endl;
#ifdef _WIN32
    // windows 下需要装载网络访问库
    WSADATA lpWSAData;
    WSAStartup(MAKEWORD(2, 2), &lpWSAData);
#endif

    // 1. 创建嵌套字
    SOCKET toSocket = createSocket();
    if (toSocket == -1)
    {
        return;
    }

    // 2. 链接到服务器
    int connectRet = connectToHost(toSocket, "127.0.0.1", 9999);
    if (connectRet == -1)
    {
        closeSocket(toSocket);
        return;
    }

    // 3. 通信
    ifstream file("d:\\test.txt", ifstream::in);
    string text;
    while (getline(file, text))
    {
        cout << "客户端发送数据(" << text.length() << "):" << text << endl;
        sendMsg(toSocket, text.c_str(), text.length());
        usleep(300);
    }

    // int fd = open("d:\\test.txt", OF_READ);
    // int length = 0;
    // char tmp[100];
    // while ((length = read(fd, tmp, sizeof(tmp)) > 0))
    // {
    //     cout << "客户端发送数据(" << sizeof(tmp) << "):" << tmp << endl;
    //     sendMsg(toSocket, tmp, sizeof(tmp));
    //     memset(tmp, 0, sizeof(tmp));
    //     usleep(300);
    // }
    sleep(10);

    // close(fd);
    file.close();
    closeSocket(toSocket);

#ifdef _WIN32
    // windows 平台下结束了还需要移除网络库的装载
    WSACleanup();
#endif
}

int setListen(SOCKET serverSocket, unsigned short port)
{
    sockaddr_in socketAddr;
    socketAddr.sin_family = AF_INET;
    socketAddr.sin_addr.s_addr = INADDR_ANY;
    socketAddr.sin_port = htons(port);
    int bindRet = ::bind(serverSocket, (sockaddr *)&socketAddr, sizeof(socketAddr));
    if (bindRet == -1)
    {
        cerr << "绑定端口失败！！！" << endl;
        return -1;
    }
    cout << "端口绑定成功！！！" << endl;

    int listenRet = listen(serverSocket, 128);
    if (listenRet == -1)
    {
        cerr << "监听失败！！！" << endl;
        return -1;
    }
    cout << "端口监听成功！！！" << endl;
    return listenRet;
}

SOCKET acceptConn(SOCKET serverSocket, sockaddr_in *clientAddr)
{
    SOCKET clientSocket = -1;

    if (clientAddr == NULL)
    {
        // 如果不需要获取客户端的地址信息可以不传接收地址
        clientSocket = accept(serverSocket, NULL, NULL);
    }
    else
    {
        // 如果希望接受到客户端的地址信息则传如一个地址结构体
        addrlen clientAddrlen = sizeof(sockaddr_in);
        clientSocket = accept(serverSocket, (sockaddr *)clientAddr, &clientAddrlen);
    }

    if (clientSocket == -1)
    {
        cerr << "服务器接受客户端异常！！！" << endl;
        return clientSocket;
    }

    cout << "服务器成功接受到客户端！！！" << endl;

    return clientSocket;
}

int connectToHost(SOCKET toSocket, const char *ip, unsigned short port)
{
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(ip);
    serverAddr.sin_port = htons(port);

    int connectRet = connect(toSocket, (sockaddr *)&serverAddr, sizeof(serverAddr));
    if (connectRet == -1)
    {
        cerr << "客户端链接到服务器失败！" << endl;
        return connectRet;
    }

    cout << "客户端链接到服务器成功!!" << endl;

    return connectRet;
}

SOCKET createSocket()
{
    SOCKET sk = socket(AF_INET, SOCK_STREAM, 0);
    if (sk == -1)
    {
        cerr << "socket 创建失败！！" << endl;
        return -1;
    }
    cout << "socket 创建成功！！" << endl;
    return sk;
}

// 有粘包问题的版本
// int sendMsg(SOCKET toSocket, const char *msg, int len)
// {
//     int ret = send(toSocket, msg, len, 0);
//     if (ret == -1)
//     {
//         cout << "发送数据异常！！！" << endl;
//     }
//     return ret;
// }

// 有粘包问题的版本
// int recvMsg(SOCKET fromSocket, char *msg, int size)
// {
//     int len = recv(fromSocket, msg, size, 0);
//     if (len == 0)
//     {
//         cout << "客户端已断开！！！" << endl;
//     }
//     else if (len < 0)
//     {
//         cout << "接受数据异常！！！" << endl;
//     }
//     return len;
// }

// 解决粘包问题的版本，但这个版本虽然回将粘包拆包，
// 但由于服务器和客户端数据发送的频率不一致，有可能导致缓冲区存满，服务器无法及时处理完而导致客户端某个包被拆分发送了，形成半包的问题。
// int sendMsg(SOCKET toSocket, const char *msg, int len)
// {
//     if (toSocket <= 0 || len <= 0 || msg == NULL)
//         return -1;
//     const int dataLen = len + (sizeof(int) / sizeof(char));
//     char data[dataLen];
//     int packetLen = htonl(len);
//     memcpy(data, &packetLen, sizeof(packetLen));
//     memcpy(data + sizeof(packetLen), msg, len);
//
//     int ret = send(toSocket, data, dataLen, 0);
//     if (ret == -1)
//     {
//         cout << "发送数据异常！！！" << endl;
//     }
//
//     return ret;
// }

// 解决粘包问题的版本，但这个版本虽然回将粘包拆包，
// 但由于服务器和客户端数据发送的频率不一致，有可能导致缓冲区存满，服务器无法及时处理完而导致客户端某个包被拆分发送了，形成半包的问题。
// int recvMsg(SOCKET fromSocket, char *msg, int size)
// {
//     int packetLen = 0;
//     recv(fromSocket, (char *)&packetLen, sizeof(int), 0);
//     packetLen = ntohl(packetLen);
//     cout << "数据包大小：" << packetLen << endl;
//
//     int len = recv(fromSocket, msg, packetLen, 0);
//     if (len == 0)
//     {
//         cout << "客户端已断开！！！" << endl;
//     }
//     else if (len < 0)
//     {
//         cout << "接受数据异常！！！" << endl;
//     }
//     return len;
// }

// 解决粘包与半包的问题
int sendData(SOCKET toSocket, const char *data, int len)
{
    const char *tmpData = data;
    int count = len;
    while(count > 0)
    {
        int sendLen = send(toSocket, tmpData, count, 0);
        
        if (sendLen == -1)
        {
            cout << "发送数据异常！！！" << endl;
            return sendLen;
        } 
        else if(sendLen == 0)
        {
            continue;
        }
        tmpData += sendLen;
        count -= sendLen;
    }

    return len;
}

int sendMsg(SOCKET toSocket, const char *msg, int len)
{
    if (toSocket <= 0 || len <= 0 || msg == NULL)
        return -1;
    const int dataLen = len + (sizeof(int) / sizeof(char));
    char data[dataLen];
    int packetLen = htonl(len);
    memcpy(data, &packetLen, sizeof(packetLen));
    memcpy(data + sizeof(packetLen), msg, len);

    return sendData(toSocket, data, dataLen);
}

// 解决粘包与半包的问题
int recvData(SOCKET fromSocket, char *data, int size)
{   
    char* tmpData = data;
    int count = size;
    while(count > 0) {
        int recvLen = recv(fromSocket, tmpData, size, 0);
        if (recvLen == 0)
        {
            cout << "客户端已断开！！！" << endl;
            return size - count;
        }
        else if (recvLen < 0)
        {
            cout << "接受数据异常！！！" << endl;
            return recvLen;
        }
        
        tmpData += recvLen;
        count -= recvLen;
    }

    return size;
}

int recvMsg(SOCKET fromSocket, char *msg, int size)
{   
    // 获取数据包头
    int packetLen = 0;
    recvData(fromSocket, (char *)&packetLen, sizeof(int));
    packetLen = ntohl(packetLen);
    cout << "数据包大小：" << packetLen << endl;

    // 获取数据体
    char packetBody[packetLen + 1]; // 此处为了保持函数签名一致所以不适用malloc 来动态分配内存。而是使用memcpy来拷贝， 性能有待商榷。
    int sendLen = recvData(fromSocket, packetBody, packetLen);
    if(sendLen != packetLen)
    {
        return -1;
    }
    packetBody[packetLen] = '\0';
    memcpy(msg, packetBody, packetLen + 1);

    return sendLen;
}

int closeSocket(SOCKET socket)
{
    int closeRet = compatCloseSocket(socket);
    if (closeRet == -1)
    {
        cerr << "socket 关闭失败！！！" << endl;
    }
    return closeRet;
}
